#pragma once 

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <mutex>
#include <unordered_map>
#include "util.hpp"
#include "log.hpp"

namespace ns_index{
  struct DocInfo{
    std::string title;    //文档的标题
    std::string content;  //文档去标签后的内容
    std::string url;      //官方文档
    uint64_t  doc_id;     //文档的ID
  };

  struct InvertedElem{
    uint64_t doc_id;
    std::string word;
    int weight;
  };
  
  //倒排拉链
  typedef std::vector<InvertedElem> InvertedList;

  class Index{
    private:
      //正排索引的数据结构使用数组，因为数组的下标就是天然的ID
      std::vector<DocInfo> forward_index; //正排索引
      //倒排索引是一个关键字和一组InvertedElem对应[关键字和倒排拉链之间的映射关系]
      std::unordered_map<std::string, InvertedList> inverted_index;
    private: 
      Index(){};
      Index(const Index&) = delete;
      Index& operator=(const Index&) = delete;
    public:
      ~Index(){};
    public:
      static Index* GetInstance();
      DocInfo* GetForwardIndex(uint64_t doc_id);              //根据doc_id找到文档内容
      InvertedList* GetInvertedList(const std::string& word); //根据关键字string，获得倒排拉链
      //根据去标签，格式化后的文档，构建正排和倒排索引 data/raw_html/raw.txt
      bool BuildIndex(const std::string &input);              //parse处理完毕的数据交给我
    private:
      DocInfo* BuildForwardIndex(const std::string& line);    //根据处理过的一行数据构建正排索引
      bool BuildInvertedIndex(const DocInfo& doc);            //处理整理好的doc 建立倒排索引
      static Index* instance;                                 //获取单例
  };
  Index* Index::instance = nullptr;

  Index* Index::GetInstance(){
    static std::mutex mtx;
    if (nullptr == instance){
      mtx.lock();
      if (nullptr == instance){
        instance = new Index;
      }
      mtx.unlock();
    }
    return instance;
  }


  DocInfo* Index::GetForwardIndex(uint64_t doc_id){
    if (doc_id >= forward_index.size()){
      std::cerr << "doc_id out range, error!" << std::endl;
      return nullptr;
    }
    return &forward_index[doc_id];
  }             
  InvertedList* Index::GetInvertedList(const std::string& word){
    auto iter = inverted_index.find(word);
    if (iter == inverted_index.end()){
      std::cerr << word << "have no InvertedList" << std::endl;
      return nullptr;
    }
    return &(iter->second);
  }
  
  bool Index::BuildIndex(const std::string &input){
    std::ifstream in(input, std::ios::in | std::ios::binary);
    if (!in.is_open()){
      std::cerr << "sorry, " << input << "open error" << std::endl;
      return false;
    }
    std::string line;
    int count = 0;
    while (std::getline(in, line)){
      DocInfo* doc = BuildForwardIndex(line);
      if (doc == nullptr){
        std::cerr << "build " << line << "error" << std::endl; //for debug
        continue;
      }
      BuildInvertedIndex(*doc);
      count++;
      if (count % 50 == 0){
        //std::cout << "当前已经建立的索引文档:" << count << std::endl;
        LOG(NORMAL, "当前已经建立索引文档: " + std::to_string(count));
      } 
    }
    LOG(NORMAL, "索引建立完毕");
    return true;
  }      


  DocInfo* Index::BuildForwardIndex(const std::string& line){
    //1. 解析line, 字符串进行切分
    std::vector<std::string> results;
    const std::string sep = "\3";
    ns_util::StringUtil::Split(line, &results, sep);
    if (results.size() != 3){
      return nullptr;
    }
    //2. 字符串进行填充到DocInfo 
    DocInfo doc;
    doc.title = results[0]; 
    doc.content = results[1];
    doc.url = results[2];
    doc.doc_id = forward_index.size();  //想要插入的数组下标就是未插入时的元素个数
    //3. 插入到正排索引的vector 
    forward_index.push_back(doc);
    return &forward_index.back();
  }   

  bool Index::BuildInvertedIndex(const DocInfo& doc){
    //DocInfo{title, content, url, doc_id}
    //word -> 倒排拉链
    struct word_cnt{
      int title_cnt;
      int content_cnt;
      word_cnt(): title_cnt(0), content_cnt(0){}
    };
    std::unordered_map<std::string, word_cnt> word_map; //用来暂存词频的映射表

    //对文档标题进行分词
    std::vector<std::string> title_words;
    ns_util::JiebaUtil::CutString(doc.title, &title_words);
    //for debug
    //if (doc.doc_id == 1572){
    //  for (auto &s : title_words){
    //    std::cout << "title: " << s << std::endl;
    //  }
    //}
    
    //对文档标题词频统计
    
    for (std::string &s : title_words){
      word_map[s].title_cnt++;
    }

    //对文档内容进行分词
    std::vector<std::string> content_words;
    ns_util::JiebaUtil::CutString(doc.content, &content_words);

    //for debug
    //if (doc.doc_id == 1572){
    //  for (auto &s : content_words){
    //    std::cout << "title: " << s << std::endl;
    //  }
    //}
    
    //对文档内容词频统计
    for (std::string &s : content_words){
      boost::to_lower(s); //将分词全部转化成小写
      word_map[s].content_cnt++;
    }

#define X 10
#define Y 1
    //相关性的建立(有待提升)
    for (auto& word_pair : word_map){
      InvertedElem item;
      item.doc_id = doc.doc_id;
      item.word = word_pair.first;
      item.weight = X * word_pair.second.title_cnt + Y * word_pair.second.content_cnt; //相关性
      InvertedList &inverted_list = inverted_index[word_pair.first];
      inverted_list.push_back(item);
    }
    return true;
  }           




}
